import java.io.File
import java.nio.file.Files
import java.nio.file.Paths

/*************************/
/*   main run entrypoint   */
/*************************/
/*
fun main() {
    println("hello world")
    print("hello world")
}
*/

/*
fun main(args: Array<String>) {
    print(args.contentToString())
}
*/


/*************************/
/* visibility modifier 可见性关键字 */
/*************************/
/**
 * public 声明的类、变量、方法默认情况下是public的，所有地方均可引用
 * internal 在同一模块下(一个maven或者gradle中新建的一个模块)，对其他类可见
 * private 仅在同一个文件内可见
 * protected 与private类似，但是其子类也可见
 *
 */


/*************************/
/* function */
/*************************/
fun sum(a: Int, b: Int) = a + b  // 简易写法

fun sum1(a: Int = 1, b: Int = 2) : Int { // default param value
    return a + b + 1
}

fun printSum(a: Int, b: Int): Unit {  // Unit return type == void(java)
    print("sum of $a + $b is ${a + b}")
}

fun String.cut(index: Int): Char {
    return this.codePointAt(index).toChar()
}






/*************************/
/* variables declare */
/*************************/
val PI = 3.14 // 全局不可变变量
const val MAX_VAL = 8 // 全局静态不可变, const修饰的是编译时就确定的常亮
fun localVar() {
    val a: Int = 1 // val 不可变
    val b = 2  // 类型推断
    var c: Int  // 先声明后初始化
    c = 3
    c ++
    print("a = $a, b = $b, c = ${ c * PI}")

    // lazy init
    val p: String by lazy {
        "hello world"
    }

}



/*************************/
/* classes and instances */
/*************************/
// 任何类在kotlin中默认继承自Any, 默认所有没有修饰的类为final，不可继承，如果需要继承，增加open关键字
class Rect(var height: Double, var length: Double) {
    var perimeter = (height + length) * 2

//    constructor() {  // 构造函数  ==  init {}
//        perimeter += 1
//    }
}

// 等效于下面这种
class Rect1(height: Double, length: Double) {
    var perimeter = 0.0

    init {
        perimeter = 2 * (height + length)
    }
}

fun cal() {
    val rect = Rect(1.3, 2.3)
    println(rect.perimeter)
}

abstract class Animal(var name: String) {
    abstract fun eat()
    fun pee() {  // 无访问修饰符情况下方法为final不可重写
        print("pee")
    }
    open fun run() {  // open修饰符修饰表示可被子类重写
        print("run")
    }
}

class Dog: Animal("dog") {  // Animal("dog") 相当于构造函数

    override fun eat() {
        TODO("Not yet implemented")
    }

    override fun run() {
        print("dog run")
    }
}

class Bird(name: String): Animal(name) {  // 继承父类狗在函数
    override fun eat() {
        TODO("Not yet implemented")
    }

}

// Cat constructor(age: Int) = Cat(age: Int)  构造函数
class Cat constructor(var age: Int = 1): Animal("cat") {

    init {  // 相当于构造函数中的初始化块
        age = 10
    }

    override fun eat() {
        super.pee()  // 调用父类方法
    }

}

// object表达式，直接作为匿名类对象
val pig = object {
    val name = "pig"
    fun eat() {
        print("eat")
    }
}

// 匿名类 object 表达式，继承类作为匿名类
val cat = object : Animal("cat") {
    override fun eat() {
        print("cat eat")
    }
}

// 单例模式
object Single {
    val c = Customer2("tom")

    fun hello() {

    }
}

fun hello() {
    cat.eat()
    print(Single.c.email)
    Single.hello()
}



class Cow(name: String = "bull") { // 主构造函数

    init { // init块相当于主构造函数内容，早于body内其他的二级构造函数
        print(1)
    }

    // 二级构造函数，必须继承主构造函数, 二级构造函数的参数全部默认为val且不可指定
    constructor(name:String, age: Int): this(name) {
        print("$name $age")
    }
}

class DragonFly: Animal {

    // 如果类没写柱构造函数，二级构造函数需要继承父类，使用super
    constructor(name: String): super(name) {
        print(name)
    }

    override fun eat() {
        TODO("Not yet implemented")
    }

}

class Shit {}  // 默认生成无参构造，默认的主无参构造函数是public

open class Food {} // open修饰可被继承，否则默认final不可继承

class Pee private constructor() { // 指定主构造函数为私有构造，外部无法通过构造函数初始化
    // object声明，object声明表达式在class内部需要使用companion
    // companion object代码块内的方法或者变量看上去像是java中的静态类型
    // 但实际上是实例类型，companion object可以继承类
    // 如果需要块内解释为为Java静态，需要使用@JvmStatic标注
    // companion object块类似于java中静态块会在其外部类被加载是初始化
    companion object {  // 伴随对象块， 在第一次访问时初始化
        fun instance() = Pee()
    }
}

fun classDemo() {
    Shit()
    Pee.instance()
}

interface Shape {
    val count: Int
    fun perimeter()
}

class Circle(override val count: Int = 0): Shape {
    override fun perimeter() {
        print("Rect")
    }
}  // 设置继承到的变量值

open class Triangle: Shape {
    override val count: Int = 3  // 也可以在此处初始化
    override fun perimeter() {
        TODO("Not yet implemented")
    }
}

interface Polygon {
    fun perimeter() {
        TODO("Not yet implemented")
    }
}

open class Sector {
    open fun perimeter() {
        TODO("Not yet implemented")
    }
}
// 单继承+接口实现，
class OuterClass : Sector(), Polygon {

    private val height = 10
    private val width = 10

    // count属性重写get方法
    var count = 0
        get() = height * width
        private set  //  set方法为private， 变成了只读属性
    //  等同于
    // val count = height * width

    override fun perimeter() {
        super<Sector>.perimeter()  // 父类有相同方法时调用父类方式
        super<Polygon>.perimeter()
    }

    inner class InnerClass() {
        fun innerHi() {
            println("inner $count")
            perimeter()  // 直接调用外部类方法
            super<Sector>@OuterClass.perimeter()  // 调用外部类的父类非抽象属性或方法
        }
    }

}


// 枚举类, 枚举类可以继承接口，但是不能继承类，其他用法和类类似
enum class Color {
    RED,GREEN
}

inline fun <reified T: Enum<T>> printValues() {
    // 打印指定类型的枚举类
    print(enumValues<T>().joinToString { "${it.ordinal} - ${it.name}" })
}


// 只读属性
class A {
    private val _list = mutableListOf(1, 3)
    // 只读，只对外暴漏get读取特性
    val list: List<Int>
        get() = _list
}

// 注解声明
@Target(AnnotationTarget.PROPERTY)
annotation class HiAnno

@HiAnno  // 注解使用
var x: String = ""


/*

    Idioms 习惯、约定俗称
    kotlin 中的编程习惯

 */

// data 关键字标注的类为数据类DO, 默认会生成equal hashCode toString copy等方法
// kotlin编译器会自动根据主构造函数内的参数编译生成类
data class Customer(val name: String, val email: String)  // 主构造函数中的属性在equal和hashCode等方法中被使用

data class Customer2(private var name: String) {
    var email: String = "abc@123.com"  // body体内的属性编译器将不会在生成class时用在equal hashCode等方法内
}

data class Customer3(val name: String, val address: String) {

    fun printAddress() {
        println("address is $address")
    }

    fun isInstance(obj: Any): Boolean {
        return when(obj) {
            is Customer -> true
            is Customer2 -> true
            else -> false
        }
    }
}


/**
 * 密封类 sealed， 是一种限制外部子类实现的方式
 * 1. 密封类本身为abstract抽象类， 可以由抽象方法和属性
 * 2. 密封类内的方法和属性只能为private和protected两种
 * 3. 只有模块内确定的子类，外部不能继承实现
 * 4. 密封类直接子类必须和密封类位于同一个sourceSet的同一个包package
 * 5. 子类必须为命名类，密封类不存在匿名子类
 * 以上局限性只限于密封类直接子类
 */
sealed class FuckError

class FuckNormalError: FuckError() {
    val times = 0
}


fun dataClassDemo() {
    val c = Customer("hello", "world")
    val (name, email) = c // data类对象支持解包 ， kotlin中也存在Pair、 Triple类
    print("$name $email")
}


// invoke Demo01Basic
fun a() {
    hello()
}

// ? 与 ?:
fun notNullQuestionMark() {
    val files = File("text").listFiles()
    // if not null shorthand
    print(files?.size)

    // 如果files是空返回empty，否则执行files.size
    println(files?.size ?: "empty")

    // if-null-else 语法, 如果files为null返回empty，否则返回files
    val a = files ?: "empty"

    // = if files not null
    files?.let {
        print(it.size)
    }

    // 如果集合不为空返回第一个，否则返回empty
    print(listOf("").firstOrNull() ?: "empty")
}


// try - catch
fun test1() {
    val result = try {
        notNullQuestionMark()
    } catch (e: Exception) {
        throw e
    }

    val n = result ?: 1
    val result1 = if (n == 1) {
        "hello"
    } else if (n == 2) {
        "world"
    } else {
        "3"
    }

}


// 构建器模式用法
fun builderDemo(size: Int): IntArray {
    // 初始化创建一个Int数组，并且放入一个值12
    return IntArray(size).apply { fill(12) }
}

// with 作用域 用法
fun withDemo() {
    val dog = Dog()
    with(dog) {
        eat()
        pee()
    }

    // 初始化属性 apply
    dog.apply {
        name = "abc"
    }
}


// try-with-resources语法
fun tryWithResDemo() {
    val stream = Files.newInputStream(Paths.get("/abc.txt"))
    // use 作用域 结束后自动关闭流
    stream.buffered().reader().use { reader ->
        print(reader.readText())
    }
}

// 泛型
class Tech {
    // 静态方法
    companion object {
        fun <T: Any, I: Any> convert(clz: Class<I>): T? {
            return null
        }
    }

}
// reified关键字只能用于inline函数, 用于具化参数类型。 Dog.to是对Dog对象增加的扩展方法
inline fun <reified T: Any> Dog.to(): T? = Tech.convert(T::class.java)
// 不适用reified具化关键字，无法直接使用T作为class使用
fun <T: Any> Dog.to1(clazz: Class<T>): T? = Tech.convert(clazz)

// 针对泛型类型，还有in/out关键字，用于限制泛型类型，相当于java中的extend / super
// in = <? super Class>,  out = <? extend Class>  来限定类边界
fun <T> copy(from: Array<out T>, to: Array<T>) {
    // from 的类型应该为 to 数组中的子类
    // 也就是from中的上限应该为to类型中的T
    // 此时 T 被称为协变类型参数
}

fun <T> fill(dest: Array<in T>, value: T) {
    // value 放入到 dest数组，value应为Array的类型的父类
    // 也就是value的类型下限是Array中实际类型T
    // 此时 T 被称为逆变类型参数
}

// * 投影, * 可以代表 in/out，类似于java中的raw类型，
fun hi(dest: Array<*>) {

}

// 多上限限制，where关键字
// 代表copyWhenGreater 中的T必须是CharSequence和Comparable的子类
// 也就是子类必须实现CharSequence和Comparable
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
        where T : CharSequence,
              T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}


// companion对象的方法扩展，相当于静态方法扩展
fun Tech.Companion.hi() {
    print("hello from Companion Object Extension")
}

fun demoReified() {
    Dog().to<Dog>() // 具化后，可以直接通过泛型不再需要class参数
    Dog().to1(Dog::class.java) // 不使用reified具化，需要参数指定class具体类型

    Tech.hi() // companion 对象的方法扩展
}


// 变量置换
fun swapDemo() {
    var a = 1
    var b = 2
    a = b.also { b = a }
}

// 标记待完善方法 TODO("")
fun someTodo(): Unit = TODO("unimplemented")

/*************************/
/* condition expressions */
/*************************/
fun maxOf(a: Int, b: Int): Int {
    println(a in 1..10)  // a是否在1到10范围内
    if (a > b) {
        return a
    } else {
        return b
    }
}

fun maxOf1(a: Int, b: Int) = if (a > b) a else b  // 简易写法, 当body体逻辑简单可以直接写在一行

fun castDemo(obj: Any) {
    println(obj is String) // is 相当于java中的instanceOf
    if (obj is String) {
        println(obj.length) // 隐式类型转换
    }
    println(obj as Long) // 显示转换，obj为null会出现npe,类型不匹配会出现CastException
    println(obj as? Double) // 避免obj为null, 但是类型不匹配会出现CastException
}


fun loopDemo() {
    val items = listOf("a", "b", "c")
    for (item in items) {
        print(item)
    }

    for (index in items.indices) {
        print("index: $index, value: ${items[index]}")
    }

    for ((idx, value) in items.withIndex()) {
        print("$idx, $value")
    }

    var index = 0
    while (index < items.size) {
        print("index: $index, value: ${items[index]}")
        index ++
    }

    do {
        print(index)
        index --
    } while (index > 0)

}

// label 执行点
fun labelDemo() {
    loop1@ for (i in 1..10) {
        print(i)
        for (j in 5..8) {
            if (j == i) {
                break@loop1
            }
        }
    }

    listOf(1, 2, 3, 4, 5).forEach for1@{
        if (it == 3) return@for1  // 返回当前循环，直接return不加label返回labelDemo方法
    }

    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // 返回当前循环，使用默认label名，默认label名=方法名
    }

    fun a1(): Int {
        return@a1 123  // 代表返回123作为返回值
    }

}

fun whenDemo(obj: Any): String {
    return when (obj) {
        1,2 -> "one"
        "two" -> "two"
        is Long -> "long"
        !is String -> "not string"
        else -> "fuck"
    }
}

fun rangeDemo() {
    val x = 10
    val y = 9
    if (x in 1..y+1) {
        print("x in range")
    }

    val list = listOf(1, 2, 3, 4)
    if (-1 !in 0..list.lastIndex) {
        println("-1 out of range")
    }
    if (list.size !in list.indices) {
        println("list.size not in range of indices")
    }

    for (i in 1..5) {
        println(i)
    }

    for (i in 1 until 100) {
        println(i)
    }

    for (i in 1..10 step 2) {
        println(i)
    }

    for (i in 9 downTo 0 step 3) {
        println(i)
    }

    for (i in 10 downTo 2) {
        println(i)
    }
}


/*************************/
/* collections  */
/*************************/
fun collectionDemo() {

    val list = listOf(1, 3)
    val result = when {
        1 in list -> "a"
        2 in list -> "b"
        else -> "fuck"
    }

    val fruits = listOf("apple", "banana", "avocado")

    fruits.filter { it.startsWith("a") }
        .sortedBy { it }
        .map { it.toUpperCase() }
        .forEach { println(it) }


    val map = mapOf("A" to 1, "B" to 2, "c" to 3)
    println(map["A"])
    for ((k, v) in map) {
        println("$k -> $v")
    }
}


/*************************/
/*  null values checks */
/*************************/
fun returnNullOrInt(a: Any?): Int? {
    if (a is Int) {
        if (a == 1) {
            return a
        } else {
            return null
        }
    } else if (a is String) {
        return a.length
    }

    // try - catch
    val b = try {
        a.hashCode()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    // throw 在kotlin中也是表达式所以可以这样用，如果b是null抛出异常
    val c = b ?: throw IllegalArgumentException("")

    return if (a !is String) 1 else 2
}

/*************************/
/*  type alias */
/*************************/
// 类型别名，作为一个已存在的类型别名，比如比较长的类可以使用简称
typealias StringMap = HashMap<String, String>

val sm = StringMap()

// 方法的别名,  其实有点类似于java中的lambda，不过可以给lambda起一个别名
typealias MyHandler = (String, Int) -> String

fun say(handler: MyHandler, name: String, age: Int) {
    handler(name, age)
}

fun sayDemo() {
    val myHandler: MyHandler = { name, age -> "hello $name, $age"}
    say(myHandler, "tom", 11)
}


